import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.minecraft.server.*;

public class etc {
	private static Logger a = Logger.getLogger("Minecraft");
	private static etc instance;
	private ArrayList<String> bannedIps = new ArrayList<String>();
	private ArrayList<String> muted = new ArrayList<String>();
	private Dictionary<String, User> users = new Hashtable<String, User>();
	private ArrayList<Kit> kits = new ArrayList<Kit>();
	private int IReallyHateJava = -1;
	private MinecraftServer server;
	private String[] reserveSlots = null;
	public String usersLoc = "users.txt", bannedLoc = "banned.txt", kitsLoc = "kits.txt", wpLoc = "waypoints.txt";
	public String adminColor = "§c", modColor = "§b", builderColor = "§a", defaultColor = "";
	public String[] allowedItems = null;
	public String[] disallowedItems = null;
	public String[] motd = null;
	private String[] playerWhitelist = null;
	public boolean itemCommandAllowed = false;
	public boolean hasVoteBan = false;
	public boolean hasVoteKick = false;
	public boolean teleportForEveryone = false;
	public boolean awayForEveryone = false;
	public boolean listWpForEveryone = false;
	public boolean reserveSlotsEnabled = false;
	public boolean canSetHome = false;
	public int playerLimit = 24;
	public int itemLimit = 16;
	
	public class Waypoint {
		public Waypoint(String wp)
		{
			String[] locs = wp.split(" ");
			if(locs.length == 3)
			{
				try {
					xLoc = Double.parseDouble(locs[0]);
					yLoc = Double.parseDouble(locs[1]);
					zLoc = Double.parseDouble(locs[2]);
				} catch (NumberFormatException e) {
					Logger.getLogger("Minecraft").log(Level.SEVERE, "Exception parsing waypoint", e);
				}
			} else if(locs.length == 4)
			{
				try {
					name = locs[0];
					xLoc = Double.parseDouble(locs[1]);
					yLoc = Double.parseDouble(locs[2]);
					zLoc = Double.parseDouble(locs[3]);
				} catch (NumberFormatException e) {
					Logger.getLogger("Minecraft").log(Level.SEVERE, "Exception parsing waypoint", e);
				}
			}
		}
		public String toString()
		{
			try {
				StringBuilder builder = new StringBuilder();
				if(name != "home")
				{
					builder.append(name);
					builder.append(" ");
				}
				builder.append(xLoc);
				builder.append(" ");
				builder.append(yLoc);
				builder.append(" ");
				builder.append(zLoc);
				return builder.toString();
			} catch (Exception e2) {
				a.log(Level.SEVERE, "Exception while building waypoint string", e2);
			}
			return "";
		}
		public String name;
		public double xLoc = 0;
		public double yLoc = 0;
		public double zLoc = 0;
	}
	
	public class Kit {
		public String Name;
		public Dictionary<Integer, Integer> IDs;
		public int Delay;
		public int Group;
	}
	
	public class User {
		public int Group;
		public String[] IPs;
		public Dictionary<String, Waypoint> waypoints = new Hashtable<String, Waypoint>();
		public Waypoint spawnPoint = new Waypoint("");
		public User(MinecraftServer server)
		{
			this.spawnPoint.name = "home";
			this.spawnPoint.xLoc = SMPAdmin.getInstance().spawnX();
			this.spawnPoint.yLoc = SMPAdmin.getInstance().spawnY();
			this.spawnPoint.zLoc = SMPAdmin.getInstance().spawnZ();
			this.Group = 0;
		}
	}
	
	private etc(MinecraftServer server) {
		this.server = server;
		
		if (!(new File(usersLoc).exists())) {
			FileWriter writer = null;
			try {
				writer = new FileWriter(usersLoc);
				writer.write("#Add your users here (When adding your entry DO NOT include #!)\r\n");
				writer.write("#The formats are:\r\n");
				writer.write("#NAME:GROUP:IP\r\n");
				writer.write("#NAME:GROUP\r\n");
				writer.write("#The groups are:\r\n");
				writer.write("#0 - Default\r\n");
				writer.write("#1 - Builder\r\n");
				writer.write("#2 - Mod\r\n");
				writer.write("#3 - Admin\r\n");
				writer.write("#Examples:\r\n");
				writer.write("#Admin1:3:127.0.0.1\r\n");
				writer.write("#Moderator39:2:10.10.10.10\r\n");
				writer.write("#BobTheBuilder:1\r\n");
			} catch (Exception e) {
				a.log(Level.SEVERE, "Exception while creating " + usersLoc, e);
			} finally {
				if (writer != null) {
					try {
						writer.close();
					} catch (IOException e) {
						a.log(Level.SEVERE, "Exception while closing writer for " + usersLoc, e);
					}
				}
			}
		}
		
		if (!(new File(kitsLoc).exists())) {
			FileWriter writer = null;
			try {
				writer = new FileWriter(kitsLoc);
				writer.write("#Add your kits here. Example entry below (When adding your entry DO NOT include #!)\r\n");
				writer.write("#miningbasics:1,2,3,4:6000\r\n");
				writer.write("#The formats are (Find out more about groups in " + usersLoc + ":\r\n");
				writer.write("#NAME:IDS:DELAY\r\n");
				writer.write("#NAME:IDS:DELAY:GROUP\r\n");
				writer.write("#6000 for delay is roughly 5 minutes.\r\n");
			} catch (Exception e) {
				a.log(Level.SEVERE, "Exception while creating " + kitsLoc, e);
			} finally {
				if (writer != null) {
					try {
						writer.close();
					} catch (IOException e) {
						
					}
				}
			}
		}

		if (!(new File(wpLoc).exists())) {
			FileWriter writer = null;
			try {
				writer = new FileWriter(wpLoc);
				writer.write("#Don't edit this file\r\n");
			} catch (Exception e) {
				a.log(Level.SEVERE, "Exception while creating " + wpLoc, e);
			} finally {
				if (writer != null) {
					try {
						writer.close();
					} catch (IOException e) {
						
					}
				}
			}
		}
		this.reload();
	}
	
	public static etc getInstance(MinecraftServer server) {
		if (instance == null)
			instance = new etc(server);
		return instance;
	}
	
	public String getPlayerName(String name) {
		for (Enumeration i = users.keys(); i.hasMoreElements();)
		{
			String pname = i.nextElement().toString();
			if(name.equalsIgnoreCase(pname))
				return pname;
		}
		Player player = null;
		for (int i = 0; i < this.server.f.b.size(); i++) {
			Player localdq = new Player(this.server.f.b.get(i));
			if (localdq.name().equalsIgnoreCase(name)) {
				player = localdq;
			}
		}
		
		if (player != null) {
			return player.name();
		}
		return null;
	}

	public boolean isOnReserve(String name) {
		for (String str : reserveSlots)
			if (str.equals(name))
				return true;
		return false;
	}
	
	public boolean isBannedIp(String ip) {
		String derp = ip.split(":")[0];
		if (derp.startsWith("/"))
			derp = derp.substring(1);
		return bannedIps.contains(derp);
	}
	
	public void addBannedIp(String name, String ip) {
		try {
			String derp = ip.split(":")[0];
			if (derp.startsWith("/"))
				derp = derp.substring(1);
			BufferedWriter bw = new BufferedWriter(new FileWriter(bannedLoc, true));
			bw.newLine();
			bw.write("#" + name);
			bw.newLine();
			bw.write(derp);
			bw.close();
			bannedIps.add(derp);
		} catch (Exception e) {
			a.log(Level.SEVERE, "Exception while reading " + bannedLoc, e);
		}
	}
	
	public int getUserGroup(Player e) {
		try {
			String derp = e.ipAddress();
			if (derp.contains("/"))
				derp = derp.substring(1);
			User user = users.get(e.name());
			String[] ips = user.IPs;
			for (String ip : ips)
				if (ip.equals(derp) || (ip.equals("") && ips.length == 1))
					return user.Group;
		} catch (Exception e1) {
			// a.info("Exception while checking admin: " + e1.getMessage());
		}
		return 0;
	}
	
	public boolean isUserInGroup(Player e, int group) {
		try {
			String derp = e.ipAddress();
			if (derp.contains("/"))
				derp = derp.substring(1);
			User user = users.get(e.name());
			if (user.Group >= group) {
				String[] ips = user.IPs;
				for (String ip : ips)
					if (ip.equals(derp) || (ip.equals("") && ips.length == 1))
						return true;
			}
		} catch (Exception e1) {
			// a.info("Exception while checking admin: " + e1.getMessage());
		}
		if (group != 0)
			return false;
		else
			return true;
	}
	
	public boolean isAdmin(Player e) {
		return isUserInGroup(e, 3);
	}
	
	public boolean isMod(Player e) {
		return isUserInGroup(e, 2);
	}
	
	public boolean isBuilder(Player e) {
		return isUserInGroup(e, 1);
	}

	public String listWaypoints(String player)
	{
		player = getPlayerName(player);
		StringBuilder builder = new StringBuilder();
		try {
			for (Enumeration i = users.get(player).waypoints.keys(); i.hasMoreElements();)
			{
				builder.append(i.nextElement().toString());
				if(i.hasMoreElements())
					builder.append(", ");
			}
		} catch (Exception e1) {
		}
		return builder.toString();
	}

	public Waypoint getWaypoint(String player, String name)
	{
		player = getPlayerName(player);
		try {
			return users.get(player).waypoints.get(name);
		} catch (Exception e1) {
		}
		return null;
	}
	
	public void setWaypoint(Player e, String name) {
		User user = null;
		try {
			user = users.get(e.name());
		} catch (Exception e1) {
		}
		
		if (user == null) {
			// User doesn't exist, add it to the array.
			try {
				user = new User(server);
				users.put(e.name(), user);
			} catch (Exception e2) {
				a.log(Level.SEVERE, "Exception while adding user to array", e2);
				return;
			}
		}
		Waypoint wp = null;
		try {
			wp = user.waypoints.get(name);
		} catch (Exception e1) {
		}
		if(wp == null)
		{
			wp = new Waypoint("");
			user.waypoints.put(name, wp);
			wp.name = name;
		}
		wp.xLoc = e.x();
		wp.yLoc = e.y();
		wp.zLoc = e.z();
		writeWaypoint(e, user);
	}
	
	public void removeWaypoint(Player e, String name) {
		User user = null;
		try {
			user = users.get(e.name());
		} catch (Exception e1) {
		}
		
		if (user == null) {
			// User doesn't exist, add it to the array.
			try {
				user = new User(server);
				users.put(e.name(), user);
			} catch (Exception e2) {
				a.log(Level.SEVERE, "Exception while adding user to array", e2);
				return;
			}
		}
		try {
			user.waypoints.remove(name);
		} catch (Exception e1) {
		}
		writeWaypoint(e, user);
	}

	private void writeWaypoint(Player e, User user) {
		try {
			BufferedReader reader = new BufferedReader(new FileReader(new File(wpLoc)));
			String line = "", text = "", newline = "";
			boolean foundIt = false;
			StringBuilder builder = new StringBuilder();
			builder.append(e.name());
			builder.append(":");
			builder.append(user.spawnPoint.toString());
			builder.append(":");
			for (Enumeration i = user.waypoints.elements(); i.hasMoreElements();)
			{
				builder.append(i.nextElement().toString());
				builder.append(":");
			}
			newline = builder.toString();
			while ((line = reader.readLine()) != null) {
				if (!line.contains(e.name() + ":"))
					text += line + "\r\n";
				else {
					if(!foundIt)
					{
						foundIt = true;
						text += newline + "\r\n";
					}
				}
			}
			if(!foundIt)
				text += newline + "\r\n";
			reader.close();
			
			FileWriter writer = new FileWriter(wpLoc);
			writer.write(text);
			writer.close();
		} catch (Exception e1) {
			a.log(Level.SEVERE, "Exception while editing waypoints in " + wpLoc, e);
		}
	}
	
	public void changeGroup(Player e, int group, boolean ips) {
		User user = null;
		try {
			user = users.get(e.name());
		} catch (Exception e1) {
		}
		
		if (user != null) {
			try {
				user = users.get(e.name());
				// User exists, otherwise it exceptions.
				user.Group = group;
				// Now to save...
				BufferedReader reader = new BufferedReader(new FileReader(new File(usersLoc)));
				String line = "", text = "";
				while ((line = reader.readLine()) != null) {
					if (!line.contains(e.name()))
						text += line + "\r\n";
					else {
						StringBuilder builder = new StringBuilder();
						builder.append(e.name());
						builder.append(":");
						builder.append(user.Group);
						if (user.IPs.length > 0 && !user.IPs[0].equals("") && ips) {
							builder.append(":");
							builder.append(SMPAdmin.combineSplit(0, user.IPs, ","));
						}
						text += builder.toString() + "\r\n";
					}
				}
				reader.close();
				
				FileWriter writer = new FileWriter(usersLoc);
				writer.write(text);
				writer.close();
			} catch (Exception e1) {
				a.log(Level.SEVERE, "Exception while editing user in " + usersLoc, e);
			}
		} else {
			// User doesn't exist, woo.
			try {
				BufferedWriter bw = new BufferedWriter(new FileWriter(usersLoc, true));
				user = new User(server);
				user.Group = group;
				user.IPs = new String[] { "" };
				StringBuilder builder = new StringBuilder();
				builder.append(e.name());
				builder.append(":");
				builder.append(user.Group);
				if (ips) {
					String derp = e.ipAddress();
					if (derp.contains("/"))
						derp = derp.substring(1);
					
					builder.append(":");
					builder.append(derp);
				}
				bw.newLine();
				bw.append(builder.toString());
				bw.close();
				users.put(e.name(), user);
			} catch (Exception e2) {
				a.log(Level.SEVERE, "Exception while writing new user to " + usersLoc, e);
			}
		}
	}
	
	public boolean hasKits() {
		return kits.size() > 0;
	}
	
	public String getKitNames(Player e) {
		StringBuilder builder = new StringBuilder();
		int group = getUserGroup(e);
		for (Kit kit : kits)
			if (group >= kit.Group)
				builder.append(kit.Name + " ");
		return builder.toString();
	}
	
	public Kit getKit(String name) {
		for (Kit kit : kits)
			if (kit.Name.equalsIgnoreCase(name))
				return kit;
		return null;
	}
	
	public void reload() {
		try {
			itemCommandAllowed = server.d.a("itemcommandallowed", "false").equalsIgnoreCase("true");
			allowedItems = server.d.a("alloweditems", "").split(",");
			disallowedItems = server.d.a("disalloweditems", "").split(",");
			reserveSlots = server.d.a("reserveslots", "").split(",");
			playerWhitelist = server.d.a("playerwhitelist", "").split(",");
			motd = server.d.a("motd", "Type /help for a list of commands.").split("@");
			hasVoteBan = server.d.a("votebanenabled", "false").equalsIgnoreCase("true");
			hasVoteKick = server.d.a("votekickenabled", "false").equalsIgnoreCase("true");
			playerLimit = Integer.parseInt(server.d.a("playerlimit", "24"));
			itemLimit = Integer.parseInt(server.d.a("itemlimit", "16"));
			reserveSlotsEnabled = server.d.a("reserveslotsenabled", "false").equalsIgnoreCase("true");
			teleportForEveryone = server.d.a("teleportforeveryone", "false").equalsIgnoreCase("true");
			awayForEveryone = server.d.a("awayforeveryone", "false").equalsIgnoreCase("true");
			listWpForEveryone = server.d.a("listwpforeveryone", "false").equalsIgnoreCase("true");
			canSetHome = server.d.a("canchangehome", "false").equalsIgnoreCase("true");
			usersLoc = server.d.a("admintxtlocation", "users.txt");
			bannedLoc = server.d.a("bannedtxtlocation", "banned.txt");
			kitsLoc = server.d.a("kitstxtlocation", "kits.txt");
			wpLoc = server.d.a("wptxtlocation", "waypoints.txt");
			adminColor = server.d.a("admincolor", "§c");
			modColor = server.d.a("modcolor", "§b");
			builderColor = server.d.a("buildercolor", "§a");
			defaultColor = server.d.a("defaultcolor", "");
		} catch (Exception e) {
			a.log(Level.SEVERE, "Exception while reading from server.properties", e);
			// Just in case...
			disallowedItems = new String[] { "" };
			allowedItems = new String[] { "" };
			reserveSlots = new String[] { "" };
			motd = new String[] { "Type /help for a list of commands." };
		}
		
		if (new File(bannedLoc).exists()) {
			try {
				bannedIps = new ArrayList<String>();
				Scanner scanner = new Scanner(new File(bannedLoc));
				while (scanner.hasNextLine()) {
					String line = scanner.nextLine();
					if (line.startsWith("#") || line.length() == 0)
						continue;
					bannedIps.add(line);
				}
				scanner.close();
			} catch (Exception e) {
				a.log(Level.SEVERE, "Exception while reading " + bannedLoc, e);
			}
		}
		
		if (new File(usersLoc).exists()) {
			try {
				users = new Hashtable<String, User>();
				Scanner scanner = new Scanner(new File(usersLoc));
				while (scanner.hasNextLine()) {
					String line = scanner.nextLine();
					if (line.startsWith("#") || line.length() == 0)
						continue;
					
					String[] split = line.split(":");
					if(split.length < 2)
					{
						a.log(Level.SEVERE, "This line doesn't have enough info in it: " + line);
						continue;
					}
					String name = line.split(":")[0];
					String ip = "";
					if (split.length == 3)
						ip = split[2];
					User user = new User(server);
					user.Group = Integer.parseInt(split[1]);
					user.IPs = ip.split(",");
					
					users.put(name, user);

					if(user.Group == 3)
						SMPAdmin.getInstance().makeOp(name);
				}
				scanner.close();
			} catch (Exception e) {
				a.log(Level.SEVERE, "Exception while reading " + usersLoc + " (Are you sure you formatted it correctly?)", e);
			}
		}
		
		if (new File(kitsLoc).exists()) {
			try {
				kits = new ArrayList<Kit>();
				Scanner scanner = new Scanner(new File(kitsLoc));
				while (scanner.hasNextLine()) {
					String line = scanner.nextLine();
					if (line.startsWith("#") || line.length() == 0)
						continue;
					String[] split = line.split(":");
					String name = split[0];
					String[] ids = split[1].split(",");
					int delay = Integer.parseInt(split[2]);
					int group = 0;
					if (split.length == 4)
						group = Integer.parseInt(split[3]);
					Kit kit = new Kit();
					kit.Name = name;
					kit.IDs = new Hashtable<Integer, Integer>();
					for (String str : ids) {
						int id = -1;
						int amount = 1;
						if (str.contains(" ")) {
							id = Integer.parseInt(str.split(" ")[0]);
							amount = Integer.parseInt(str.split(" ")[1]);
						} else
							id = Integer.parseInt(str);
						kit.IDs.put(id, amount);
					}
					kit.Delay = delay;
					kit.Group = group;
					kits.add(kit);
				}
				scanner.close();
			} catch (Exception e) {
				a.log(Level.SEVERE, "Exception while reading " + kitsLoc, e);
			}
		}
		if (new File(wpLoc).exists()) {
			try {
				Scanner scanner = new Scanner(new File(wpLoc));
				while (scanner.hasNextLine()) {
					String line = scanner.nextLine();
					if (line.startsWith("#") || line.length() == 0)
						continue;
					String[] split = line.split(":");
					if(split.length < 2)
						continue;
					String name = split[0];
					User user = null;
					try {
						user = users.get(name);
					} catch (Exception e1) {
						// do nothing!
					}
					
					if (user == null) {
						// User doesn't exist, add it to the array.
						try {
							user = new User(server);
							users.put(name, user);
						} catch (Exception e2) {
							a.log(Level.SEVERE, "Exception while adding user to array", e2);
						}
					}
					a.info("Loading in waypoints for: " + name);
					user.waypoints = new Hashtable<String, Waypoint>();
					if (split[1].length() > 5)
					{
						// they have a custom spawn point!
						user.spawnPoint = new Waypoint(split[1]);
						user.spawnPoint.name = "home";
						a.info("Loading in spawn point: " + split[1]);
					}
					for(int i = 2; i < split.length; i++)
					{
						Waypoint wp = new Waypoint(split[i]);
						user.waypoints.put(wp.name, wp);
						a.info("Loading in waypoint: " + split[i]);
					}
				}
			} catch (Exception e) {
				a.log(Level.SEVERE, "Exception while reading " + wpLoc, e);
			}
		}
	}
	
	public boolean isMuted(Player e) {
		return muted.contains(e.name());
	}
	
	public boolean toggleMute(Player e) {
		if (muted.contains(e.name()))
			muted.remove(e.name());
		else
			muted.add(e.name());
		return muted.contains(e.name());
	}
	
	public boolean isOnWhitelist(Player e) {
		for (String str : playerWhitelist)
			if (str.equalsIgnoreCase(e.name()))
				return true;
		return false;
	}
	
	public boolean hasWhitelist() {
		return playerWhitelist.length > 0 && !playerWhitelist[0].equals("");
	}
}
